package httplib

import (
	"bytes"
	"crypto/tls"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
)

type Request struct {
	url     string
	request *http.Request
	client  *http.Client
}

var transport *http.Transport

func init() {
	// 去掉所有的超时设置，因为会影响到连接池的功能
	// 具体请看：https://mp.weixin.qq.com/s/PjSM2RNpIA934KBPDnf6MA
	// client 的超时，在每次请求的时候设置即可
	transport = &http.Transport{
		// 忽略证书
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		Proxy:           http.ProxyFromEnvironment,
		MaxIdleConns:    800, // 连接池对所有host的最大连接数量，默认无穷大
	}
}

func NewRequest(urlStr, method string) IRequest {
	return NewRequestWithClient(urlStr, method, &http.Client{
		Transport: transport,
	})
}

func NewRequestWithClient(urlStr, method string, client *http.Client) IRequest {
	instance := &Request{
		urlStr,
		&http.Request{
			Method:     method,
			URL:        nil,
			Proto:      "HTTP/1.1",
			ProtoMajor: 1,
			ProtoMinor: 1,
			Header:     make(http.Header),
			Body:       nil,
		},
		client,
	}
	instance.request.Header.Set("Device", "")
	instance.request.Header.Set("Connection", "Keep-Alive")
	return instance
}

func (r *Request) SetHeader(key, value string) *Request {
	r.request.Header.Set(key, value)
	return r
}

func (r *Request) SetCookie(cookie *http.Cookie) *Request {
	r.request.AddCookie(cookie)
	return r
}

func (r *Request) SetTimeout(timeout time.Duration) *Request {
	if timeout >= 1000 && timeout <= 30*time.Second {
		r.client.Timeout = timeout
	}
	return r
}

func (r *Request) SetQueryParams(v map[string]string) *Request {
	if !strings.Contains(r.url, "?") {
		r.url += "?"
	}
	values := make(url.Values)
	for key := range v {
		values.Set(key, v[key])
	}
	r.url += values.Encode()
	return r
}

func (r *Request) SetBody(body io.Reader) *Request {
	rc, ok := body.(io.ReadCloser)
	if !ok && body != nil {
		rc = ioutil.NopCloser(body)
	}
	r.request.Body = rc
	switch v := body.(type) {
	case *bytes.Buffer:
		r.request.ContentLength = int64(v.Len())
		buf := v.Bytes()
		r.request.GetBody = func() (io.ReadCloser, error) {
			re := bytes.NewReader(buf)
			return ioutil.NopCloser(re), nil
		}
	case *bytes.Reader:
		r.request.ContentLength = int64(v.Len())
		snapshot := *v
		r.request.GetBody = func() (io.ReadCloser, error) {
			re := snapshot
			return ioutil.NopCloser(&re), nil
		}
	case *strings.Reader:
		r.request.ContentLength = int64(v.Len())
		snapshot := *v
		r.request.GetBody = func() (io.ReadCloser, error) {
			re := snapshot
			return ioutil.NopCloser(&re), nil
		}
	default:
	}
	return r
}

func (r *Request) SetContentType(contentType string) *Request {
	r.request.Header.Set("Content-Type", contentType)
	return r
}

func (r *Request) Do() (*Response, error) {
	// url处理
	u, _ := url.Parse(r.url)
	r.request.URL = u
	r.request.Host = strings.TrimSuffix(u.Host, ":")
	// body检测
	if r.request.GetBody != nil && r.request.ContentLength == 0 {
		r.request.Body = http.NoBody
		r.request.GetBody = func() (io.ReadCloser, error) { return http.NoBody, nil }
	}
	rs, err := r.client.Do(r.request)
	if nil != err {
		return nil, err
	}
	body, err := ioutil.ReadAll(rs.Body)
	rs.Body.Close()
	if nil != err {
		return nil, err
	}
	return &Response{body, rs.StatusCode, &rs.Header, rs.Cookies()}, err
}
